package com.ck.company.service.impl;

import com.ck.cache.RedisCacheDao;
import com.ck.common.service.OperationLogService;
import com.ck.company.service.CpDeviceInfoService;
import com.ck.constant.*;
import com.ck.dao.DeviceMapper;
import com.ck.dao.DeviceSettingMapper;
import com.ck.dao.ProgramMapper;
import com.ck.exception.ServiceException;
import com.ck.model.common.DeviceSetting;
import com.ck.model.common.OperationLog;
import com.ck.model.common.User;
import com.ck.model.common.bo.DeviceReportBO;
import com.ck.model.common.bo.DeviceStateChangeBO;
import com.ck.model.common.vo.ProgramSimpleVO;
import com.ck.model.company.dto.CpDeviceDetailDTO;
import com.ck.model.company.vo.*;
import com.ck.util.DateUtils;
import com.ck.util.RegionUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

@Service(value = "cpDeviceInfoService")
public class CpDeviceInfoServiceImpl implements CpDeviceInfoService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    DeviceMapper deviceMapper;
    @Autowired
    ProgramMapper programMapper;
    @Autowired
    DeviceSettingMapper deviceSettingMapper;
    @Autowired
    private RedisCacheDao redisCacheDao;
    @Autowired
    private OperationLogService operationLogService;

    @Override
    public Page<CpDeviceInfoListVO> listByPage(String companyCode, Long groupId, String deviceNum, String deviceIp, String deviceMac,
                                               String deviceName, Integer deviceStatus, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return deviceMapper.listByPageForCp(companyCode, groupId, deviceNum, deviceIp, deviceMac, deviceName, deviceStatus);
    }

    @Override
    public Long getOnlineDeviceInfoCount(String companyCode) {
        return getDeviceCountByConditions(companyCode, BusinessConstant.DEVICE_INFO_STATUS_ON_LINE, null, null, null, null);
    }

    @Override
    public Long getOfflineDeviceInfoCount(String companyCode) {
        return getDeviceCountByConditions(companyCode, BusinessConstant.DEVICE_INFO_STATUS_OFF_LINE, null, null, null, null);
    }

    private Long getDeviceCountByConditions(String companyCode, Integer status, String deviceNum, String deviceIp, String deviceMac, Long groupId) {
        return deviceMapper.getCpDeviceCountByConditions(companyCode, status, deviceNum, deviceIp, deviceMac, groupId);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void updateDeviceGroupNull(String companyCode, Long groupId) {
        deviceMapper.updateDeviceGroupNull(companyCode, groupId);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void shutdown(String deviceCodes, String groupIds, User user, String ip) throws ServiceException {
        DeviceSetting setting = new DeviceSetting();
        setting.setShutDownOnce(Boolean.TRUE);
        deviceSettingAndSaveLog(DeviceSettingType.SHUT_DOWN, deviceCodes, groupIds, user, ip, setting);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void restart(String deviceCodes, String groupIds, User user, String ip) throws ServiceException {
        DeviceSetting setting = new DeviceSetting();
        setting.setRestartOnce(Boolean.TRUE);
        deviceSettingAndSaveLog(DeviceSettingType.RESTART, deviceCodes, groupIds, user, ip, setting);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setBootAndOffTimmer(String deviceCodes, String groupIds, User user, String ip, String bootTime,
                                    String offTime) throws ServiceException {
        DeviceSetting setting = new DeviceSetting();
        setting.setTimeBoot(bootTime);
        setting.setTimeShutdown(offTime);
        deviceSettingAndSaveLog(DeviceSettingType.BOOT_OFF_TIMER, deviceCodes, groupIds, user, ip, setting);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setScreenOnAndOffTimmer(String deviceCodes, String groupIds, User user, String ip, String bootTime,
                                        String offTime) throws ServiceException {
        DeviceSetting setting = new DeviceSetting();
        setting.setTimeBootScreen(bootTime);
        setting.setTimeShutdownScreen(offTime);
        deviceSettingAndSaveLog(DeviceSettingType.SCREEN_TIMER, deviceCodes, groupIds, user, ip, setting);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setVolume(String deviceCodes, String groupIds, User user, String ip, Integer vol)
            throws ServiceException {
        DeviceSetting setting = new DeviceSetting();
        setting.setDeviceVolume(vol);
        deviceSettingAndSaveLog(DeviceSettingType.SET_VOLUME, deviceCodes, groupIds, user, ip, setting);
    }

    /**
     * 根据终端mac或者机器码查询终端详情
     *
     * @param companyCode 商家code
     * @param deviceMac   终端mac
     * @param deviceCode  终端机器码
     * @return
     */
    @Override
    public CpDeviceDetailVO getDeviceDetailByDeviceMacOrDeviceCode(String companyCode, String deviceMac,
                                                                   String deviceCode) {
        List<CpDeviceDetailDTO> list = deviceMapper.getDeviceDetailByDeviceMacOrDeviceCode(companyCode, deviceMac, deviceCode);
        CpDeviceDetailDTO dto = CollectionUtils.isNotEmpty(list) ? list.get(0) : null;
        if (dto != null) {
            CpDeviceDetailVO deviceInfoVO = generateDeviceInfoVO(dto);
            setProgramSimpleInfo(dto, deviceInfoVO);
            return deviceInfoVO;
        }
        return null;
    }

    /**
     * 根据商家编号/分组id，获取对应的所有deviceCode
     *
     * @param companyCode
     * @param groupIds
     * @return List<String>
     */
    @Override
    public List<String> listAllDeviceCode(String companyCode, List<String> groupIds) {
        return deviceMapper.listAllDeviceCode(companyCode, groupIds);
    }

    /**
     * @param settingType
     * @param deviceCode
     * @param groupIds
     * @param user
     * @param ip
     */
    private void deviceSettingAndSaveLog(DeviceSettingType settingType, String deviceCode, String groupIds, User user, String ip, DeviceSetting setting) throws ServiceException {
        String companyCode = user.getCompanyCode();
        String operationMsg = "";
        if(StringUtils.isNotBlank(deviceCode)){
            operationMsg = String.format("操作行为：%s. 操作对象：[终端:%s]", settingType.getSettingType(), deviceCode);
        } else if(StringUtils.isNotBlank(groupIds)){
            operationMsg = String.format("操作行为：%s. 操作对象：[分组:%s]", settingType.getSettingType(), groupIds);
        }
        logger.info(operationMsg);
        List<String> deviceCodes = null;
        //分组id为空
        if (StringUtils.isBlank(groupIds)) {
            String splitChar = ",";
            deviceCodes = Arrays.asList(deviceCode.split(splitChar));
        } else {
            if (groupIds.contains(NormalStrConstant.NULL)) {
                deviceCodes = listAllDeviceCode(companyCode, null);
            } else {
                deviceCodes = listAllDeviceCode(companyCode, Arrays.asList(groupIds.split(",")));
            }
        }

        if (CollectionUtils.isNotEmpty(deviceCodes)) {
            // 记录批次号（时间戳）保证数据库跟缓存中更新时间一致
            Long serialNo = System.currentTimeMillis();

            // 1.设置信息同步到数据库
            setting.setUpdateTime(serialNo);
            updateDeviceSetting(setting, deviceCodes, companyCode);

            // 2.保存操作日志
            saveOperationLog(operationMsg, user, ip);

            // 3.将最新状态更新到缓存
            flushDeviceStateCache(companyCode, settingType, deviceCodes, serialNo);
        }
    }

    @Override
    public void flushDeviceStateCache(String companyCode, DeviceSettingType settingType, List<String> codes, Long serialNo) {

        // 将更新存储到缓存的业务，不应受事务控制（即：及时缓存更新失败，或操作过程抛出异常也应当保证数据的正常入库操作）
        try {
            for (Iterator<String> it = codes.iterator(); it.hasNext(); ) {
                String deviceCode = it.next();

                // 如果companyCode非空，则验证终端信息是否存已经存在于缓存中。如果存在则不用查询数据库，否则需要二次验证避免越权操作数据据
                if (StringUtils.isNotBlank(companyCode)) {
                    boolean matchFlag = true;
                    DeviceReportBO deviceInCache = (DeviceReportBO) redisCacheDao.getCache(CacheConstant.CACHE_NAME_DEVICE_COMPANY_PREFIX + companyCode, deviceCode);
                    if (deviceInCache == null) {
                        // 缓存中没有设备信息，则从数据库中二次查询
                        matchFlag = deviceMatchCompany(deviceCode, companyCode);
                        // 非法访问
                        if (!matchFlag) {
                            String tips = String.format("非法访问，终端code:%s与商家编号:%s不匹配", deviceCode, companyCode);
                            logger.error(tips);
                            it.remove(); // 非法数据移除
                            // TODO 是否该抛出异常？ throw new ServiceException(tips);
                            continue;
                        }
                    }
                }

                // 设备操作
                DeviceStateChangeBO deviceStateBO = (DeviceStateChangeBO) redisCacheDao.getCache(CacheConstant.CACHE_NAME_DEVICE_SETTING, deviceCode);
                if (deviceStateBO == null) {
                    deviceStateBO = new DeviceStateChangeBO();
                }
                switch (settingType) {
                    case SHUT_DOWN: // 关机
                    case RESTART: // 重启
                    case BOOT_OFF_TIMER: // 定时开关机
                    case SCREEN_TIMER: // 定时开关屏
                    case SET_VOLUME: // 设置音量
                        deviceStateBO.setNewSetting(true);
                        deviceStateBO.setNewSettingUpdateTime(serialNo);
                        break;
                    case SOFTWARE_UPGRADE: // 软件升级
                    case FIRMWARE_UPGRADE: // 固件升级
                        deviceStateBO.setNewUpgrade(true);
                        deviceStateBO.setNewUpgradeUpdateTime(serialNo);
                        break;
                    case PROGRAM_UPDATE: // 节目信息更新
                        deviceStateBO.setNewProgram(true);
                        deviceStateBO.setNewProgramUpdateTime(serialNo);
                        break;
                    default:
                        break;
                }
                redisCacheDao.putCache(CacheConstant.CACHE_NAME_DEVICE_SETTING, deviceCode, deviceStateBO);
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("flushDeviceStateCache 刷新缓存业务异常：" + e.getMessage(), e);
        }

    }

    private void saveOperationLog(String operContent, User user, String ip) {
        // 保存操作日志
        OperationLog operationLog = new OperationLog();
        operationLog.setOperationContent(operContent);
        operationLog.setOperationModule(ModuleConstant.MODULE_DEVICE_MNG);
        operationLog.setOperationStatus(BusinessConstant.OPERATION_LONG_OPERATION_STATUS_SUCCESS);
        operationLog.setOperationTime(System.currentTimeMillis());
        operationLog.setUserName(user.getUserName());
        operationLog.setRealname(user.getRealname());
        operationLog.setCopmanyCode(user.getCompanyCode());
        operationLog.setOperationIp(ip);
        operationLogService.saveOperationLog(operationLog);
    }


    private void setProgramSimpleInfo(CpDeviceDetailDTO dto, CpDeviceDetailVO vo) {

        // 记录program_version
        List<String> listAll = new ArrayList<>();
        List<String> currentDowning = new ArrayList<>();// 当前正在下载节目
        List<String> currentPlaying = new ArrayList<>();// 当前正在播放节目
        List<String> waitPlay = new ArrayList<>();// 等待播放节目
        String currentDownloadingStr = dto.getCurrentDownloadingProgramVer();
        if (StringUtils.isNotBlank(currentDownloadingStr)) {
            String[] split = currentDownloadingStr.split(",");
            currentDowning = Arrays.asList(split);
            listAll.addAll(currentDowning);
        }
        String currentPlayingStr = dto.getCurrentPlayingProgramVer();
        if (StringUtils.isNotBlank(currentPlayingStr)) {
            String[] split = currentPlayingStr.split(",");
            currentPlaying = Arrays.asList(split);
            listAll.addAll(currentPlaying);
        }
        String waitPlayStr = dto.getWaitPlayProgramVer();
        if (StringUtils.isNotBlank(waitPlayStr)) {
            String[] split = waitPlayStr.split(",");
            waitPlay = Arrays.asList(split);
            listAll.addAll(waitPlay);
        }

        // 批量查询节目基础信息
        if (!CollectionUtils.isEmpty(listAll)) {
            List<ProgramSimpleVO> programs = programMapper.getProgramSimpleInfoByVersion(listAll);
            vo.setProgramQueue(programs);
            for (ProgramSimpleVO pro : programs) {
                if (currentDowning.contains(pro.getProgramVersion())) {
                    pro.setState(ProgramStateType.CURRENT_DOWNLOADING.getProgramStateType());
                    continue;
                }
                if (currentPlaying.contains(pro.getProgramVersion())) {
                    pro.setState(ProgramStateType.CURRENT_PLAYING.getProgramStateType());
                    continue;
                }
                if (waitPlay.contains(pro.getProgramVersion())) {
                    pro.setState(ProgramStateType.WAIT_PLAY.getProgramStateType());
                    continue;
                }
            }
        }

    }

    private CpDeviceDetailVO generateDeviceInfoVO(CpDeviceDetailDTO dto) {
        CpDeviceDetailVO vo = new CpDeviceDetailVO();
        vo.setDeviceCode(dto.getDeviceCode());
        vo.setDeviceName(dto.getDeviceName());
        vo.setCreateTime(toYmdhms(dto.getCreateTime()));
        vo.setDeviceStatus(dto.getDeviceStatus() != null && dto.getDeviceStatus() == BusinessConstant.DEVICE_INFO_STATUS_ON_LINE ? "在线" : "离线");
        vo.setDeviceMac(dto.getDeviceMac());
        vo.setDeviceModel(dto.getDeviceModel());
        vo.setIp(dto.getIp());
        vo.setGroupName(dto.getGroupName());
        vo.setProvince(dto.getProvince());
        vo.setCity(dto.getCity());
        vo.setArea(dto.getArea());
        vo.setDetailAddr(dto.getDetailAddr());
        vo.setSoftwareVersion(dto.getSoftwareVersion());
        vo.setSoftwareUpgradeTime(toYmdhms(dto.getSoftwareUpgradeTime()));
        vo.setFirmwareVersion(dto.getFirmwareVersion());
        vo.setFirmwareUpgradeTime(toYmdhms(dto.getFirmwareUpgradeTime()));
        vo.setTimeBoot(dto.getTimeBoot());
        vo.setTimeShutdown(dto.getTimeShutdown());
        vo.setTimeBootScreen(dto.getTimeBootScreen());
        vo.setTimeShutdownScreen(dto.getTimeShutdownScreen());
        vo.setDeviceVolume(dto.getDeviceVolume());
        return vo;
    }

    private String toYmdhms(Long time) {
        if (time == null) {
            return "";
        }
        try {
            return DateUtils.longTimeStampToStringDate(time, DateUtils.PATTEN_YMD_HMS);
        } catch (Exception e) {
            return "";
        }
    }

    @Override
    public Page<CpProgramPutinDeviceListVO> listProgramPutinDeviceByPage(String companyCode, String deviceCode,
                                                                         String[] deviceGroupIdArr, String province,
                                                                         String city, String area, Long startTimeL,
                                                                         Long endTimeL, Integer putinLevel, Integer pageNum,
                                                                         Integer pageSize) {

        PageHelper.startPage(pageNum, pageSize);
        return deviceMapper.listProgramPutinDeviceByPage(companyCode, deviceCode, deviceGroupIdArr, province, city, area
                , startTimeL, endTimeL, putinLevel);
    }

    @Override
    public Page<CpProgramPutinDeviceListVO> listProgramPutinDeviceBySingleGroupByPage(String companyCode, String deviceCode,
                                                                         Long deviceGroupId, String province,
                                                                         String city, String area, Long startTimeL,
                                                                         Long endTimeL, Integer putinLevel, Integer pageNum,
                                                                         Integer pageSize) {

        PageHelper.startPage(pageNum, pageSize);
        return deviceMapper.listProgramPutinDeviceBySingleGroupByPage(companyCode, deviceCode, deviceGroupId, province, city, area
                , startTimeL, endTimeL, putinLevel);
    }

    @Override
    public boolean deviceMatchCompany(String code, String companyCode) {
        if (StringUtils.isNotBlank(code)) {
            String deviceCode = deviceMapper.deviceBelongToCp(code, companyCode);
            return code.equals(deviceCode);
        }
        return false;
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void updateDeviceSetting(DeviceSetting setting, List<String> deviceCodes, String companyCode) {
        deviceSettingMapper.updateDeviceSetting(setting, deviceCodes, companyCode);
    }

    /**
     * 更新终端基础信息
     *
     * @param deviceCode   code
     * @param deviceName  终端名（备注）
     * @param companyCode 商家编码
     * @param province    省
     * @param city        市
     * @param area        地区
     * @param detailAddr  详细地址
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void updateDeviceBasicInfo(String deviceCode, String deviceName, String companyCode, String province, String city, String area, String detailAddr) {
        String region = null;
        if (StringUtils.isNotBlank(province)) {
            region = RegionUtil.getProvinceRegion(province);
        }
        deviceMapper.updateDeviceBasicInfo(deviceCode, deviceName, companyCode, region, province, city, area, detailAddr);
    }

    /**
     * 全部终端分页列表
     *
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @param province    省份
     * @param city        城市
     * @param area        区/县
     * @param deviceCode  机器码
     * @param deviceMac   终端MAC
     * @param deviceGroup 所属分组
     * @param companyCode 商家代码
     * @param pageNum     当前页数
     * @param pageSize    每页条数
     * @return
     */
    @Override
    public Page<CpAllDeviceVO> allDeviceReport(String startTime, String endTime, String province, String city, String area, String deviceCode, String deviceMac, String deviceGroup, String companyCode, Integer pageNum, Integer pageSize) {
        pageNum = (pageNum == null ? PageConstant.DEFAULT_PAGE_NUM : pageNum);
        pageSize = (pageSize == null ? PageConstant.DEFAULT_PAGE_SIZE : pageSize);
        PageHelper.startPage(pageNum, pageSize);

        //把格式化字符串时间转化为Long类型时间戳
        Long startTimeL = null;
        Long endTimeL = null;
        try {
            if (StringUtils.isNotBlank(startTime)) {
                startTimeL = DateUtils.stringTimeToLongTimeStamp(startTime, DateFormatConstant.DATE_SHORT_ISO);
            }
            if (StringUtils.isNotBlank(endTime)) {
                endTimeL = DateUtils.stringTimeToLongTimeStamp(endTime, DateFormatConstant.DATE_SHORT_ISO);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return deviceMapper.allDevicePageCp(startTimeL, endTimeL, province, city, area, deviceCode, deviceMac, deviceGroup, companyCode);
    }

    /**
     * 导出全部终端列表
     *
     * @param companyCode 商家代码
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @param province    省份
     * @param city        城市
     * @param area        区/县
     * @param deviceCode  机器码
     * @param deviceMac   终端MAC
     * @param deviceGroup 所属分组
     * @return
     */
    @Override
    public List<CpAllDeviceVO> allDeviceListExport(String companyCode, String startTime, String endTime, String province, String city, String area, String deviceCode, String deviceMac, String deviceGroup) {
        //把格式化字符串时间转化为Long类型时间戳
        Long startTimeL = null;
        Long endTimeL = null;
        try {
            if (StringUtils.isNotBlank(startTime)) {
                startTimeL = DateUtils.stringTimeToLongTimeStamp(startTime, DateFormatConstant.DATE_SHORT_ISO);
            }
            if (StringUtils.isNotBlank(endTime)) {
                endTimeL = DateUtils.stringTimeToLongTimeStamp(endTime, DateFormatConstant.DATE_SHORT_ISO);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return deviceMapper.allDevicePageCp(startTimeL, endTimeL, province, city, area, deviceCode, deviceMac, deviceGroup, companyCode);
    }

    /**
     * 根据商家编码和节目版本号查询终端mac集合
     *
     * @param companyCode 商家编码
     * @param versionList 版本号集合
     * @return 终端mac集合
     */
    @Override
    public List<String> listDeviceCodeByCompanyCodeAndProgramVersion(String companyCode, List<String> versionList) {
        return deviceMapper.listDeviceCodeByCompanyCodeAndProgramVersion(companyCode, versionList);
    }

    /**
     * 单个终端分页列表
     *
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @param deviceCode  机器码
     * @param deviceMac   终端MAC
     * @param companyCode 商家代码
     * @param pageNum     当前页数
     * @param pageSize    每页条数
     * @return
     */
    @Override
    public Page<CpSingleDeviceVO> singleDeviceReport(String startTime, String endTime, String deviceCode, String deviceMac, String companyCode, Integer pageNum, Integer pageSize) {
        pageNum = (pageNum == null ? PageConstant.DEFAULT_PAGE_NUM : pageNum);
        pageSize = (pageSize == null ? PageConstant.DEFAULT_PAGE_SIZE : pageSize);
        PageHelper.startPage(pageNum, pageSize);
        //把格式化字符串时间转化为Long类型时间戳
        Long startTimeL = null;
        Long endTimeL = null;
        try {
            if (StringUtils.isNotBlank(startTime)) {
                startTimeL = DateUtils.stringTimeToLongTimeStamp(startTime, DateFormatConstant.LONG_DATE_FORMAT_STR);
            }
            if (StringUtils.isNotBlank(endTime)) {
                endTimeL = DateUtils.stringTimeToLongTimeStamp(endTime, DateFormatConstant.LONG_DATE_FORMAT_STR);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return deviceMapper.singleDevicePageCp(startTimeL, endTimeL, deviceCode, deviceMac, companyCode);
    }


    /**
     * 导出单个终端列表
     *
     * @param companyCode 商家代码
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @param deviceCode  机器码
     * @param deviceMac   终端MAC
     * @return
     */
    @Override
    public List<CpSingleDeviceVO> singleDeviceListExport(String companyCode, String startTime, String endTime, String deviceCode, String deviceMac) {
        //把格式化字符串时间转化为Long类型时间戳
        Long startTimeL = null;
        Long endTimeL = null;
        try {
            if (StringUtils.isNotBlank(startTime)) {
                startTimeL = DateUtils.stringTimeToLongTimeStamp(startTime, DateFormatConstant.DATE_SHORT_ISO);
            }
            if (StringUtils.isNotBlank(endTime)) {
                endTimeL = DateUtils.stringTimeToLongTimeStamp(endTime, DateFormatConstant.DATE_SHORT_ISO);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return deviceMapper.singleDevicePageCp(startTimeL, endTimeL, deviceCode, deviceMac, companyCode);
    }

    /**
     * 当前方法没有添加任何事物，如过遇到失败，已更新条数也不用回滚
     */
    @Override
    public int setDeviceOffLine() {
        // 考虑到网络可能有延迟且当前方法执行也有一定延时，故将最近一次上报时间超过当前时刻前一分钟的设备置为离线状态。
        Long timeDiff = System.currentTimeMillis() - 60 * 1000;
        return deviceMapper.setDeviceOffLine(timeDiff);
    }

    /**
     * 解绑终端
     *
     * @param companyCode 商家编号
     * @param deviceCodes  终端序列号（多个英文逗号隔开）
     * @param user 登录用户
     * @param ip 登录用户IP
     * @author linqiang
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void untie(String companyCode, String deviceCodes, User user, String ip) {
        List<String> deviceCodeList = null;
        //分组id为空
        if (StringUtils.isNotBlank(deviceCodes)) {
            String splitChar = ",";
            deviceCodeList = Arrays.asList(deviceCodes.split(splitChar));
            deviceMapper.untie(deviceCodeList);

            //写入解绑缓存
            for (String code : deviceCodeList) {
                redisCacheDao.deleteByKey(CacheConstant.CACHE_NAME_DEVICE_KEY_MSG, code);
            }
            // 2.保存操作日志
            saveOperationLog("解绑终端：" + deviceCodes, user, ip);
        }
    }
}
